/**
 * Based on com.rialvalue.layouts.CoverflowLayout.
 * 
 * This layout class is specific to this application so properties in the
 * original class have been replaced here by constants and can be adjusted
 * only here in the class file.
 */
package com.ettractions.kiosk.layouts
{
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.geom.Matrix3D;
    import flash.geom.PerspectiveProjection;
    import flash.geom.Point;
    import flash.geom.Vector3D;
    import flash.utils.Timer;
    
    import mx.core.ILayoutElement;
    import mx.core.IVisualElement;
    import mx.core.UIComponent;
    
    import spark.layouts.supportClasses.LayoutBase;

    public class CoverflowLayout extends LayoutBase
	{
		private static const HORIZONTAL_DISTANCE:int = 534;
		
		private static const SELECTED_ITEM_PROXIMITY:int = 1;
		
		private static const DEPTH_DISTANCE:int = 1;
		
		private static const ELEMENT_ROTATION:int = -95;
		
        private static const ANIMATION_DURATION:int = 700;
		
        private static const ANIMATION_STEPS:int = 30;
		
		private static const FOCAL_LENGTH:int = 300;
		
        private static const RIGHT_SIDE:int = -1;
		
        private static const LEFT_SIDE:int = 1;
		
        private var _finalMatrices:Vector.<Matrix3D>;
		
		private var _timer:Timer;
		
		private var _selectedIndex:int = -1;
		
		private var _centerX:Number;
		
        private var _centerY:Number;
		
		// HACK!
		public var skipTransitionOnce:Boolean = true;
		
		public function CoverflowLayout()
		{
			super();
			
			_timer = new Timer(ANIMATION_DURATION / ANIMATION_STEPS, ANIMATION_STEPS);
			_timer.addEventListener(TimerEvent.TIMER, handleTimer);
			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, handleTimerComplete);			
		}
		
		public function set selectedIndex(index:int):void
		{
			if (_selectedIndex != index)
			{
				_selectedIndex = index;
				
				target.invalidateDisplayList();
				target.invalidateSize();
			}
		}
		
		public function get selectedIndex():int
		{
			return _selectedIndex;
		}
		
        private function centerPerspectiveProjection(width:Number, height:Number):void
		{
            var perspectiveProjection:PerspectiveProjection = new PerspectiveProjection();
			
            perspectiveProjection.projectionCenter = new Point(width / 2, height / 2);
            perspectiveProjection.focalLength = FOCAL_LENGTH;

            target.transform.perspectiveProjection = perspectiveProjection;
        }

        private function positionCentralElement(element:ILayoutElement, width:Number, height:Number):Matrix3D
		{
            element.setLayoutBoundsSize(NaN, NaN, false);
			
            var matrix:Matrix3D = new Matrix3D();
            var elementWidth:Number = element.getLayoutBoundsWidth(false);
            var elementHeight:Number = element.getLayoutBoundsHeight(false);

            _centerX = (width - elementWidth) / 2;
            _centerY = (height - elementHeight) / 2;

            matrix.appendTranslation(_centerX, _centerY, -SELECTED_ITEM_PROXIMITY);

            element.setLayoutBoundsSize(NaN, NaN, false);

            if (element is IVisualElement)
                IVisualElement(element).depth = 10;

            return matrix;
        }

        private function positionLateralElement(element:ILayoutElement, index:int, side:int):Matrix3D
		{
            element.setLayoutBoundsSize(NaN, NaN, false);

            var matrix:Matrix3D = new Matrix3D();
            var elementWidth:Number = element.getLayoutBoundsWidth(false);
            var elementHeight:Number = element.getLayoutBoundsHeight(false);
            var zPosition:Number = index * DEPTH_DISTANCE;

            if (side == RIGHT_SIDE)
			{
                matrix.appendTranslation(-elementWidth, 0, 0);
                matrix.appendRotation(side * ELEMENT_ROTATION, Vector3D.Y_AXIS);
                matrix.appendTranslation(2 * elementWidth - HORIZONTAL_DISTANCE, 0, 0);
            }
			
			else
                matrix.appendRotation(side * ELEMENT_ROTATION, Vector3D.Y_AXIS);

            matrix.appendTranslation(_centerX - side * (index) * HORIZONTAL_DISTANCE, _centerY, zPosition);

            if (element is IVisualElement)
                IVisualElement(element).depth = -zPosition;

            return matrix;
        }

        override public function updateDisplayList(width:Number, height:Number):void
		{
            var i:int = 0;
            var j:int = 0;
            var numElements:int = target.numElements;
            var matrix:Matrix3D;

            if (numElements > 0)
			{
                centerPerspectiveProjection(width, height);
				
				_finalMatrices = new Vector.<Matrix3D>(numElements);

                var midElement:int = selectedIndex == -1 ? Math.ceil(numElements / 2) : selectedIndex;
				
                matrix = positionCentralElement(target.getVirtualElementAt(midElement), width, height);
				
				_finalMatrices[midElement] = matrix;

                for (i = midElement - 1; i >= 0; i--)
				{
                    matrix = positionLateralElement(target.getVirtualElementAt(i), midElement - i, LEFT_SIDE);
					
					_finalMatrices[i] = matrix;
                }

                for (j = 0, i = midElement + 1; i < numElements; i++, j++)
				{
                    matrix = positionLateralElement(target.getVirtualElementAt(i), j, RIGHT_SIDE);
					
					_finalMatrices[i] = matrix;
                }

				// HACK!
				if (skipTransitionOnce)
				{
					for (i = 0; i < numElements; i++)
						target.getVirtualElementAt(i).setLayoutMatrix3D(_finalMatrices[i], false);
					
					skipTransitionOnce = false;
				}
				
				else
				{
					_timer.reset();
					_timer.start();
				}
            }
        }

        private function handleTimer(event:TimerEvent):void
		{
            var numElements:uint = target.numElements;
			var element:ILayoutElement;
            var initialMatrix:Matrix3D;

            for (var i:int = 0; i < numElements; i++)
			{
				element = target.getVirtualElementAt(i);
				initialMatrix = UIComponent(element).transform.matrix3D;

                initialMatrix.interpolateTo(_finalMatrices[i], 0.2);
                element.setLayoutMatrix3D(initialMatrix, false);
            }
        }

		private function handleTimerComplete(event:TimerEvent):void
		{
			_finalMatrices = null;
			
			dispatchEvent(new Event(Event.COMPLETE));
		}
	}
}